--=============================================================================== --18.02.2008 - added dynamic weather --25.06.2011 - modified for AtmosFear 3 by Cromm Cruac --28.12.2013 - modified by Alundaio for all levels --8.02.2016 - modified by Alundaio to USE_MARSHAL --=============================================================================== -- Tronex: -- 2018/4/17 - global weather system -- 2018/11/8 - added new particle effects Dead Air -- 2018/11/19 - changed cycle check to be based on date instead of hours -- 2019/6/6 - cleaning, fixed bugs -- 2020/2/19 - custom weather parameter modifiers for brightness --=============================================================================== local enable_debug = false local ini_weather = ini_file("environment\\dynamic_weather_graphs.ltx") local weather_tbl = utils_data.collect_section(ini_weather, "weather_cycles") local prev_sec = 0 local diff_sec = 0 local last_amb = {} valid_levels = {} bLevelUnderground = false local big_maps = { ["jupiter"] = true, ["zaton"] = true, ["pripyat"] = true, ["l12_stancia"] = true, ["l12_stancia_2"] = true, ["k00_marsh"] = true, } -------------------------------------- class "WeatherManager" function WeatherManager:__init() --self:Print("__init") self.weather_file = nil self.transition_period = false self.pre_blowout_period = false self.curr_weather = "" self.next_weather = "" self.last_hour = 0 self.next_hour = 0 self.last_period_change_date = game.get_game_time() self.forced_weather_change_on_time_change = false self.wfx_time = 0 self.weather_fx = nil self.weatherType = "[default]" self.update_time = 0 self.ini = ini_weather if not self.ini then printe("!ERROR when open weather_dynamic_graphs.ltx") end self.dof_factor = 1 self.presets = {} for i=1,#weather_tbl do self.presets[weather_tbl[i]] = utils_data.parse_ini_section_to_bool_array(self.ini, "cycle_" .. weather_tbl[i], true) end self.presets_pre_blowout = utils_data.parse_ini_section_to_bool_array(self.ini, "cycle_pre_blowout", true) self.weather_storage = {} self.cycle = get_random_weather() self.preset = self.presets[self.cycle] and random_key_table(self.presets[self.cycle]) or nil self.inited_time = 0 self.meteorites_particle = nil self.meteorites_on = false self.boundaries = { --distant lightning feature --map front view bounding box x/y coordinates: 1. left above 2. left bottom 3. right bottom 4. right above k00_marsh = {{-550,760},{-550,-736},{940,-737},{940,760}}, k01_darkscape = {{-494,697},{-673,-485},{447,-591},{308,678}}, k02_trucks_cemetery = {{-386,468},{-386,-468},{386,-468},{386,468}}, l01_escape = {{-464,963},{-315,-630},{403,-633},{404,965}}, l02_garbage = {{-367,315},{-367,-420},{368,-420},{368,315}}, l03_agroprom = {{-268,245},{-263,-373},{351,-377},{352,242}}, l04_darkvalley = {{-524,499},{-517,-930},{518,-934},{516,502}}, l05_bar = {{-511,511},{-511,-511},{510,-511},{511,511}}, l06_rostok = {{-511,511},{-511,-511},{511,-511},{511,511}}, l07_military = {{-443,508},{-443,-131},{196,-131},{195,507}}, l08_yantar = {{-282,137},{-282,-431},{282,-431},{286,136}}, l09_deadcity = {{-324,313},{-362,-328},{279,-328},{279,313}}, l10_limansk = {{-500,582},{-500,-416},{500,-417},{500,582}}, l10_radar = {{-318,557},{-318,-658},{890,-658},{890,557}}, l10_red_forest = {{-317,196},{-317,-503},{381,-503},{382,196}}, l11_hospital = {{-121,919},{-121,535},{-50,536},{-50,919}}, l11_pripyat = {{-628,778},{-627,-520},{670,-520},{671,779}}, l12_stancia = {{-600,850},{-600,-747},{1525,-747},{1728,849}}, l12_stancia_2 = {{-600,850},{-600,-747},{1525,-747},{1728,849}}, l13_generators = {{-499,209},{-499,-853},{540,-853},{540,209}}, zaton = {{0,-600},{400,-590},{580,-460},{800,-390},{800,830},{-445,820},{-490,715},{-567,607},{-620,423},{-622,-231},{-615,-470},{-568,-568},{-400,-590}}, jupiter = {{-660,640},{-660,-680},{660,-680},{660,640}}, pripyat = {{700,700},{-700,700},{-700,-700},{700,-700}} } self.storm_directions = { {-50,10}, {30,90}, {40,130}, {-110,-70}, {10,60}, {-140,-70}, {-40,20}, {90,160}, {-190,-140}, {10,90}, {120,170}, {-90,-20}, {30,130}, {130,170}, {-130,-70}, {-10,50}, {-170,-110}, {-80,-20}, {-190,-150}, {-60,-20}, {-60,-30}, {-190,-150}, {-80,-20}, {50,110} } self.distant_sounds = utils_data.collect_section(self.ini, "distant_storm_sounds") self.thunders = {} self.thunder_index = 1 self.distant_storm_prob = 0.5 if (is_empty(valid_levels)) then local gini = game_ini() local levels = utils_data.collect_section(gini,"level_maps_single") for i = 1,#levels do local wthr = gini:r_string_ex(levels[i],"weathers") if (wthr == "atmosfear") then valid_levels[levels[i]] = true end end end bLevelUnderground = valid_levels[level.name()] == nil SetEvent("underground", bLevelUnderground) -- Parse brightness table self.brightness = ui_options.get("video/night/brightness") or "slight" self.brightness_table = { -- self.brightness_table[brightness][weather][parameter] = { value = number , [1] = true , [2] = true ... } -- keys are hours ["bright"] = {}, ["medium"] = {}, ["slight"] = {}, ["dark"] = {}, } local r_curr = get_console_cmd(0,"renderer") local r_tbl = { ["renderer_r1"] = "r1", ["renderer_r2a"] = "r2", ["renderer_r2"] = "r2", ["renderer_r2.5"] = "r2", ["renderer_r3"] = "r3", ["renderer_r4"] = "r4", } for br,_ in pairs(self.brightness_table) do local result, param, value = "","","" local sec = "brightness_" .. br if ini_weather:section_exist(sec) then local n = ini_weather:line_count(sec) for i=0,n-1 do result, param, value = ini_weather:r_line_ex(sec,i,"","") local str = str_explode(param,"%.") local r = str[2] local w = str[3] local name = str[4] if r and w and name and value and ((r_tbl[r_curr] == r) or (r == "rr")) then local v = str_explode(value,",") if v and v[2] then if (not self.brightness_table[br][w]) then self.brightness_table[br][w] = {} end if (not self.brightness_table[br][w][name]) then self.brightness_table[br][w][name] = {} end -- Collect hours local h = str_explode(v[2],":") for ii=1,#h do self.brightness_table[br][w][name][tonumber(h[ii])] = true end -- Collect value local v1 = tonumber(v[1]) or 0 local vp = self.brightness_table[br][w][name].value self.brightness_table[br][w][name].value = vp and vp + v1 or v1 end end end end end --utils_data.print_table(self.brightness_table) end function WeatherManager:reset() -- Called after load (). Weather status already loaded. called from bind_stalker --self:Print("reset") local ini = ini_file("game.ltx") local wthr = ini:r_string_ex(level.name(),"weathers") or "[default]" self.weatherType = wthr -- prepare periods self.inited_time = game.get_game_time() self.last_hour = level.get_time_hours() self.next_hour = self.last_hour + 1 if (self.next_hour > 23) then self.next_hour = self.next_hour - 24 end -- enable distant storms local rnd = (math.random(1,100)/100) if (rnd <= self.distant_storm_prob) and valid_levels[level.name()] then self.distant_storm_on = true else self.distant_storm_on = false end self:select_weather(true) end function WeatherManager:update() if (self.___timer and time_global() < self.___timer) then return end self.___timer = time_global() + 3000 -- update every 1 hour, apply weather change if necessary if self.last_hour ~= level.get_time_hours() then --self:Print("update") self.last_hour = level.get_time_hours() self.next_hour = self.last_hour + 1 if (self.next_hour > 23) then self.next_hour = self.next_hour - 24 end self:change_period() self:select_weather(false) end if not (axr_main.weapon_is_zoomed) then self:apply_dof() end -- apply distant storms and meteorites for "atmosfear" weather type only diff_sec = math.ceil(game.get_game_time():diffSec(self.inited_time)/level.get_time_factor()) if (prev_sec ~= diff_sec) then prev_sec = diff_sec if (string.sub(self.levelWeather,1,9) == "atmosfear") then self:meteorites() self:distant_storm() end end end function WeatherManager:change_period() --self:Print("change_period") local current_date = game.get_game_time() -- if a surge is close, shift next period's last hour further. local diff_s = math.floor(surge_manager.SurgeManager._delta - current_date:diffSec(surge_manager.SurgeManager.last_surge_time)) local diff_p = psi_storm_manager and math.floor(psi_storm_manager.PsiStormManager._delta - current_date:diffSec(psi_storm_manager.PsiStormManager.last_psi_storm_time)) or 0 if (diff_s < 7200 and ui_options.get("alife/event/emission_state")) or (diff_p < 7200 and ui_options.get("alife/event/psi_storm_state")) or level.is_wfx_playing() then self.pre_blowout_period = true self:reset_change_date(false, 1*60*60) end -- if current hour is next period's last hour, switch period type, set next period's hour, transition is true if self:is_next_change_date(self.cycle) then self.cycle = self:get_next_weather_cycle(self.cycle) self.preset = self.presets[self.cycle] and random_key_table(self.presets[self.cycle]) or nil self:reset_change_date(true) self.transition_period = true end end function WeatherManager:select_weather(now) self:Print("select_weather (%s) | current hour: %s", tostring(now), tostring(level.get_time_hours()) ) -- get type ("atmosfear","indoor","[default]") local weather_valid = false local weather_cycle local wthr = self.weatherType if not (wthr) then return end -- if its atmosfear, pick proper event if (wthr == "atmosfear") then weather_valid = true local level_name = level.name() -- transition weather if (self.transition_period == true) then wthr = wthr .. "_transition" weather_cycle = "cloudy" self.transition_period = false -- pre-surge weather elseif (self.pre_blowout_period == true) then wthr = wthr .. "_pre_blowout" weather_cycle = "pre_blowout" self.pre_blowout_period = false -- normal weather else if not (self.cycle) then self.cycle = "clear" end weather_cycle = self.cycle wthr = wthr .. "_" .. weather_cycle end end self.levelWeather = wthr -- save for transition periods -- get proper weather name self.weather_file = nil if weather_valid then -- pick weather preset if self.presets[weather_cycle] then if self.preset and self.presets[weather_cycle][self.preset] then self.weather_file = self.preset else self.preset = random_key_table(self.presets[weather_cycle]) self.weather_file = self.preset end elseif (weather_cycle == "pre_blowout") then self.weather_file = random_key_table(self.presets_pre_blowout) end -- pick moon phase, and large maps profiles if (weather_cycle == "clear") or (weather_cycle == "partly") then --[[ if big_maps[level.name()] then self.weather_file = self.weather_file .. "_large" end --]] --self.weather_file = self.weather_file .. "_0" --self:get_moon_phase() end -- set up current and next weather, used for many things like distant storms/Meteorites/DOF if now then self.curr_weather = self.preset self.next_weather = self.preset else self.curr_weather = self.next_weather self.next_weather = self.preset end else self.weather_file = wthr end -- hour correction if now then self.last_hour = level.get_time_hours() self.next_hour = self.last_hour + 1 if (self.next_hour > 23) then self.next_hour = self.next_hour - 24 end end -- reset forced weather call if self.forced_weather_change_on_time_change then now = true self.forced_weather_change_on_time_change = false end -- apply weather change if (self.weather_fx) then self:Print("select_weather (%s) | weather_fx: %s", tostring(now), tostring(self.weather_fx)) level.start_weather_fx_from_time(self.weather_fx, self.wfx_time) self.weather_fx = nil self.wfx_time = 0 else if (not self.weather_file) then printe("! level_weathers: self.weather_file not generated!") elseif (weather.is_paused()) then printf("~ level_weathers: weather didn't set because lerping is paused in engine") else self:Print("select_weather (%s) | self.weather_file: %s", tostring(now), tostring(self.weather_file)) level.set_weather(self.weather_file, now) end end -- Set brightness self:set_brightness_boosts() self:Print("select_weather (%s) | cycle: %s - preset: %s - curr_weather: %s - next_weather: %s", tostring(now), weather_cycle, self.preset, self.curr_weather, self.next_weather) end function WeatherManager:get_next_weather_cycle(curr_weather) --self:Print("get_next_weather_cycle | curr_weather = " .. curr_weather) local weather_table_num = {} local avail_weather = {} -- set initiate values for (weather_table_num) for i=1,#weather_tbl do weather_table_num[weather_tbl[i]] = 0 end -- store correct occurrences for each stored weather cycle in (weather_table_num) for i=1,#self.weather_storage do for j=1,#weather_tbl do if self.weather_storage[i] == weather_tbl[j] then weather_table_num[self.weather_storage[i]] = weather_table_num[self.weather_storage[i]] + 1 break end end end -- prepare a table of allowed weather cycles to pick from for i=1,#weather_tbl do if (weather_tbl[i] ~= curr_weather) then local val = ui_options.get("video/weather/" .. weather_tbl[i] .. "_occurrence") or 3 if (tonumber(val) > 2.5) and (weather_table_num[weather_tbl[i]] < 2) then avail_weather[#avail_weather + 1] = weather_tbl[i] elseif (tonumber(val) > 1.5) and (weather_table_num[weather_tbl[i]] < 1) then avail_weather[#avail_weather + 1] = weather_tbl[i] end end end -- if no allowed weather cycles found, reset if (#avail_weather == 0) then --self:Print("get_next_weather_cycle | #avail_weather = 0") self.weather_storage = {} return get_random_weather() end --[[ for k,v in pairs(weather_table_num) do self:Print("get_next_weather_cycle | weather_table_num[" .. k .. "] = " .. v) end for i=1,#avail_weather do self:Print("get_next_weather_cycle | avail_weather[" .. i .. "] = " .. avail_weather[i]) end for i=1,#self.weather_storage do self:Print("get_next_weather_cycle | self.weather_storage[" .. i .. "] = " .. self.weather_storage[i]) end --]] local pick = avail_weather[math.random(#avail_weather)] self.weather_storage[#self.weather_storage + 1] = pick -- store new value printdbg("-level_weathers: get_next_weather_cycle | new_weather = %s", pick) return pick end function WeatherManager:reset_change_date(cycle_change, _s) --self:Print("reset_change_date = %s",tostring(cycle_change)) local Y, M, D, h, m, s, ms = 0, 0, 0, 0, 0, 0, 0 Y, M, D, h, m, s, ms = self.last_period_change_date:get(Y, M, D, h, m, s, ms) if (not cycle_change) then self.last_period_change_date:set(Y, M, D, h, m, s + _s, ms) else self.last_period_change_date = game.get_game_time() end end function WeatherManager:is_next_change_date(weather_cycle) local hour = 0 local len = ui_options.get("video/weather/" .. weather_cycle .. "_period") or 6 hour = math.random(math.ceil(len*2/3),len) local diff = math.ceil(game.get_game_time():diffSec(self.last_period_change_date)) --self:Print("is_next_change_date | current seconds difference = %s, critical difference = %s", diff, (hour*60*60)) return diff > (hour*60*60) end function WeatherManager:forced_weather_change() self.forced_weather_change_on_time_change = true --callstack() end function WeatherManager:get_hour_as_string(h) local hour_str = "" if (h > 23) then h = h-24 end if (h < 10) then hour_str = "0" .. tostring(h) .. ":00:00" else hour_str = tostring(h) .. ":00:00" end return hour_str end function WeatherManager:get_moon_phase() local months = {31,28,31,30,31,30,31,31,30,31,30,31} local g_time=game.get_game_time() local Y, M, D, h, m, s, ms = 0, 0, 0, 0, 0, 0, 0 Y, M, D, h, m, s, ms = g_time:get(Y, M, D, h, m, s, ms) local day = 365*(Y-2010)+D for mm = 1, M-1 do day = day + months[mm] end if h >=12 then day = day+1 end local moon = ui_options.get("video/night/moon_cycle") local phase if moon == 28 then phase = math.floor(math.mod(day,28)/3.5) elseif moon == 8 then phase = math.mod(day,8) else phase = string.sub(moon,5) end --atmosfear.debugMessages[3]:SetText("phase=" ..tostring(phase)) --atmosfear.debugMessages[4]:SetText("opt=" ..tostring(atmosfear.opt_moon_phase)) return tostring(phase) end function WeatherManager:set_brightness_boosts() if (type(weather.boost_reset) ~= "function") then printf("WeatherManager | weather.boost_value and weather.boost_reset don't exist") return end weather.boost_reset() -- reset all modifiers local wr = self.weather_file local br = self.brightness if not (wr and br and self.brightness_table[br]) then return end local function gather_params(param_table, br_table) if (br_table) then local hour = self.last_hour for param,v in pairs(br_table) do if v[hour] then local value = v.value if value and value ~= 0 then param_table[param] = param_table[param] and (param_table[param] + value) or value end end end end end local t = {} gather_params(t, self.brightness_table[br][wr]) gather_params(t, self.brightness_table[br]["all"]) if is_not_empty(t) then for param,value in pairs(t) do --printf("$[%s][%s] BOOSTING param: %s - value: %s", br, self.last_hour, param, value) weather.boost_value(param, value) end end end function WeatherManager:get_curr_weather() return self.cycle end function WeatherManager:get_curr_weather_preset() return self.preset end function WeatherManager:get_curr_ambient() -- Check local hr = level.get_time_hours() local wthr = level.get_weather() if (hr == last_amb.hr) and (wthr == last_amb.wthr) and last_amb.amb then return last_amb.amb end -- Read local ini = wthr and ini_file("environment\\weathers\\" .. wthr .. ".ltx") local hr_string = self:get_hour_as_string(hr) local amb = ini and hr_string and ini:section_exist(hr_string) and ini:r_string_ex(hr_string,"ambient") or "" -- Cache last_amb.hr = hr last_amb.wthr = wthr last_amb.amb = amb self:Print("get_curr_ambient | file: %s - ambient: %s", wthr, amb) return amb end function WeatherManager:apply_dof() -- not used end -------------------------------------------------------------------------------- -- METEORITES -------------------------------------------------------------------------------- function WeatherManager:launch_meteorites() if self.meteorites_particle ~= nil then self.meteorites_particle:stop() end self.meteorites_particle = particles_object("crommcruac\\meteorites") local actor_pos = db.actor:position() local particle_pos = vector():set(actor_pos.x, actor_pos.y + 500, actor_pos.z) self.meteorites_particle:play_at_pos(particle_pos) end function WeatherManager:stop_meteorites() if (self.meteorites_particle ~= nil) then self.meteorites_particle:stop() self.meteorites_particle = nil end end function WeatherManager:meteorites() if (level.get_time_hours() >= 22 or level.get_time_hours() < 4) and ((string.find(self.curr_weather,"clear") and level.get_time_minutes() <= 30) or (string.find(self.next_weather,"clear") and level.get_time_minutes() >= 30)) and (not level.is_wfx_playing()) then if self.meteorites_particle==nil then self:launch_meteorites() else local actor_pos = db.actor:position() local particle_pos = vector():set(actor_pos.x, actor_pos.y + 500, actor_pos.z) self.meteorites_particle:move_to(particle_pos,VEC_Z) end else self:stop_meteorites() end end -------------------------------------------------------------------------------- -- DISTANT STORMS -------------------------------------------------------------------------------- function WeatherManager:distant_storm() if self.distant_storm_on == nil and (string.find(self.next_weather,"cloudy") and level.get_time_minutes() >= 30) then local rnd = (math.random(1,100)/100) if (rnd <= self.distant_storm_prob) then self.distant_storm_on = true self.thunders = {} self.thunder_index = 1 else self.distant_storm_on = false end end for key,thunder in pairs(self.thunders) do local life_time = diff_sec-thunder.start_time if (life_time >= 30) then if thunder.sound ~= nil and thunder.sound:playing() then thunder.sound:stop() end self.thunders[key] = nil end if life_time >= 4 and life_time < 30 and thunder.hit == false then if thunder.sound ~= nil and thunder.sound:playing() then thunder.sound:stop() end if thunder.sound ~= nil then thunder.sound:play_at_pos(db.actor, thunder.sound_pos) thunder.sound.volume = 0.33 end thunder.hit = true end end if self.distant_storm_on == true and ((string.find(self.curr_weather,"cloudy") and level.get_time_minutes() <= 30) or (string.find(self.next_weather,"cloudy") and level.get_time_minutes() >= 30)) and (not level.is_wfx_playing()) then if self.next_thunder_at == nil then self.next_thunder_at = diff_sec + math.random(5, 10) end if self.next_thunder_at <= diff_sec then --self:lightning() self.next_thunder_at = diff_sec + math.random(5, 10) end end if self.distant_storm_on ~= nil and ((not string.find(self.next_weather,"cloudy")) and level.get_time_minutes() >= 30) then self.distant_storm_on = nil self.next_thunder_at = nil end end function WeatherManager:inside_boundaries(x, y) local inside = false if self.boundaries[level.name()] ~= nil then local vert = self.boundaries[level.name()] local i = 0 local j = #vert for i = 1, #vert do if (vert[i][2] < y and vert[j][2] >= y or vert[j][2] < y and vert[i][2] >= y) then if (vert[i][1] + (y - vert[i][2])/(vert[j][2] - vert[i][2])*(vert[j][1] - vert[i][1]) < x) then inside = not inside end end j = i end end return inside end function WeatherManager:lightning() local effect = particles_object("crommcruac\\thunderbolts_distant_00") local sound = sound_object(self.distant_sounds[math.random(1,#self.distant_sounds)]) local actor_pos = db.actor:position() local h if level.get_time_minutes() < 30 then h=level.get_time_hours() else h=level.get_time_hours() + 1 end if (h >= 24) then h = h - 24 end direction = self.storm_directions[h + 1] local angle_dec = math.random(direction[1],direction[2]) local angle_rad = math.rad(angle_dec) local current_far_distance = weather.get_value_numric("fog_distance") local distance = current_far_distance - 50 local safe_distance = current_far_distance local sound_distance = 100 local pos_x = math.sin(angle_rad)*distance local pos_z = math.cos(angle_rad)*distance local safe_pos_x = math.sin(angle_rad)*safe_distance local safe_pos_z = math.cos(angle_rad)*safe_distance local sound_pos_x = math.sin(angle_rad)*sound_distance local sound_pos_z = math.cos(angle_rad)*sound_distance if self:inside_boundaries(actor_pos.x + safe_pos_x, actor_pos.z + safe_pos_z) then local particle_pos = vector():set(actor_pos.x + pos_x, actor_pos.y + 10, actor_pos.z+pos_z) local sound_pos = vector():set(actor_pos.x + sound_pos_x, actor_pos.y, actor_pos.z + sound_pos_z) self.thunders[self.thunder_index] = {effect = effect, particle_pos = particle_pos, sound = sound, sound_pos = sound_pos, start_time = diff_sec, hit = false} self.thunders[self.thunder_index].effect:play_at_pos(self.thunders[self.thunder_index].particle_pos) self.thunder_index = self.thunder_index + 1 else --atmosfear.debugMessages[13]:SetText("(" ..tostring(self.thunder_index) .. ") beyond the map - not launched") end end -------------------------------------------------------------------------------- -- SAVE/LOAD -------------------------------------------------------------------------------- function WeatherManager:load_state(m_data) if not (m_data.weather_manager) then return end self.levelWeather = m_data.weather_manager.levelWeather self.cycle = m_data.weather_manager.cycle or get_random_weather() -- new self.preset = m_data.weather_manager.preset or (self.presets[self.cycle] and random_key_table(self.presets[self.cycle])) or nil -- new self.weather_storage = m_data.weather_manager.weather_storage or {} -- new self.last_period_change_date = m_data.weather_manager.last_period_change_date and utils_data.CTime_from_table(m_data.weather_manager.last_period_change_date) or game.get_game_time() if (not bLevelUnderground) then self.update_time = m_data.weather_manager.update_time self.weather_fx = m_data.weather_manager.weather_fx self.wfx_time = m_data.weather_manager.weather_fx_time or 0 end if (self.levelWeather == "atmosfear_transition") then self.transition_period = true elseif (self.levelWeather == "atmosfear_pre_blowout") then self.pre_blowout_period = true end printdbg("# LOADING: level_weather | cycle: %s - preset: %s - is_underground: %s - weather_storage size: %s", self.cycle, tostring(self.preset), tostring(bLevelUnderground), #self.weather_storage) end function WeatherManager:save_state(m_data) m_data.weather_manager = {} m_data.weather_manager.levelWeather = self.levelWeather m_data.weather_manager.cycle = self.cycle -- new m_data.weather_manager.preset = self.preset -- new m_data.weather_manager.weather_storage = self.weather_storage -- new m_data.weather_manager.last_period_change_date = self.last_period_change_date and utils_data.CTime_to_table(self.last_period_change_date) or game.get_game_time() if (not bLevelUnderground) then m_data.weather_manager.update_time = self.update_time if (level.is_wfx_playing()) then m_data.weather_manager.weather_fx = level.get_weather() m_data.weather_manager.weather_fx_time = level.get_wfx_time() or 0 end end printdbg("# SAVING: level_weather | cycle: %s - preset: %s - is_underground: %s - weather_storage size: %s", self.cycle, tostring(self.preset), tostring(bLevelUnderground), #self.weather_storage) end function WeatherManager:finalize() if (self.meteorites_particle and self.meteorites_particle:playing()) then self.meteorites_particle:stop() end for key,thunder in pairs(self.thunders) do if (thunder.effect and thunder.effect:playing()) then thunder.effect:stop() end end end function WeatherManager:Print(fmt, ...) if enable_debug then printf("WeatherManager [%s] [%s] | " .. fmt, self.preset, self.last_hour, ...) end end -------------------------------------------------------------------------------- -- Utilities -------------------------------------------------------------------------------- local weather_manager = nil function get_weather_manager() --self:Print("get_weather_manager") weather_manager = weather_manager or WeatherManager() return weather_manager end function get_next_state(graph,state) local sum = 0 for st,prob in pairs(graph) do sum = sum+prob end local rnd = (math.random(1,100)/100)*sum local next_state for st,prob in pairs(graph) do next_state = st rnd = rnd-prob if (rnd <= 0) then break end end return next_state end function get_random_weather() local tbl = {} for i=1,#weather_tbl do local a = ui_options.get("video/weather/" .. weather_tbl[i] .. "_occurrence") or 3 if (a > 1) then tbl[#tbl+1] = weather_tbl[i] --self:Print("get_random_weather | " .. weather_tbl[i]) end end if (#tbl > 0) then return tbl[math.random(#tbl)] end return weather_tbl[math.random(#weather_tbl)] end -------------------------------------------------------------------------------- -- Callbacks -------------------------------------------------------------------------------- local function update_settings() local wm = get_weather_manager() -- Update brightness local br_new = ui_options.get("video/night/brightness") local br_prev = wm and wm.brightness if (br_new ~= br_prev) then wm.brightness = br_new wm:set_brightness_boosts() end end function on_game_start() RegisterScriptCallback("on_option_change",update_settings) end